Bun.plugin: check for exception when coercing 'target' to a string#31560
Bun.plugin: check for exception when coercing 'target' to a string#31560robobun wants to merge 1 commit into
Conversation
The target option was coerced with toStringOrNull() without checking the throw scope afterwards. When the value's Symbol.toPrimitive returns an object, the coercion throws a TypeError but leaves the exception pending, and setupBunPlugin continued on to call the setup function, tripping assertNoException. Check the throw scope after toStringOrNull() and value() so the exception propagates to JS instead of crashing.
|
Updated 10:09 PM PT - May 28th, 2026
❌ @robobun, your commit 3c36de6 has 1 failures in
🧪 To try this PR locally: bunx bun-pr 31560That installs a local version of the PR into your bun-31560 --bun |
|
No open issues were found that this PR would fix. The bug (unchecked exception after 🤖 Generated with Claude Code |
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Run ID: 📒 Files selected for processing (2)
WalkthroughThe PR improves exception safety when the Bun plugin processes its ChangesPlugin target exception safety
🚥 Pre-merge checks | ✅ 4✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Comment |
|
This PR may be a duplicate of:
🤖 Generated with Claude Code |
|
Closing as a duplicate of #31286, which already fixes this same crash in |
There was a problem hiding this comment.
LGTM — straightforward addition of RETURN_IF_EXCEPTION checks following the existing pattern, with a regression test.
Extended reasoning...
Overview
This PR adds two RETURN_IF_EXCEPTION(throwScope, {}) checks in setupBunPlugin (src/jsc/bindings/BunPlugin.cpp) — one after targetValue.toStringOrNull(globalObject) and one after targetJSString->value(globalObject). Both calls can leave a pending JSC exception (the first when Symbol.toPrimitive/toString coercion throws, the second on OOM during rope resolution), and the previous code fell through to call setup() with a live exception, tripping ExceptionScope::assertNoException(). A regression test is added in test/js/bun/plugin/plugins.test.ts next to the existing invalid-target test.
Security risks
None. This is defensive exception propagation in option parsing for Bun.plugin(). It turns a debug-assert crash into a normal thrown TypeError. No new attack surface, auth, crypto, or privilege-related code is touched.
Level of scrutiny
Low. The change is 4 added lines in C++ that apply the exact same RETURN_IF_EXCEPTION macro already used dozens of times in this file (including immediately above for getIfPropertyExists). The refactor from if (auto* x = ...) to a separate declaration + check + if (x) is mechanical and preserves behavior for the non-throwing path. There are no design decisions or behavioral changes for valid inputs.
Other factors
- Found by Fuzzilli; PR description includes a clear repro and before/after.
- Regression test directly exercises the failing path and asserts the propagated error message.
- The bug-hunting system found no issues.
- No prior reviewer comments to address.
What
Bun.plugin({ target, setup })coerced thetargetoption to a string withtoStringOrNull()but never checked the throw scope afterwards.toStringOrNull()returnsnullptrand leaves a pending exception when the coercion fails — for example when the value'sSymbol.toPrimitivereturns an object (which throws aTypeError). Because the pending exception was never checked,setupBunPluginfell through, built the builder object, and called the user'ssetup()function with an exception already live. That trippedExceptionScope::assertNoException():JSString::value()can also throw (OOM while resolving a rope), so that is checked too.Repro
After
The exception propagates to JS as expected instead of crashing:
Valid targets (
"node"/"bun"/"browser"), an absent target, and the existing invalid-target error path are unaffected.Test
Added a regression test in
test/js/bun/plugin/plugins.test.tsnext to the existing invalid-target test.Found by Fuzzilli.